/*
 Copyright (c) Whitemice Consulting, 2007, 2008, 2009
 Adam Tauno Williams <awilliam@whitemice.org>
 License: MIT/X11

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
*/
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using Whitemice.ZOGI;
using Whitemice.ZOGI.GtkWidgets;
using Whitemice.ZOGI.GtkFolios;
using Whitemice.ZOGI.GtkForms;
using Gtk;

namespace Whitemice.Consonance
{
  public partial class ContactCRUD : Gtk.Window, IEntityWindow, IZOGIWidget
  { 
    protected ContactQueryForm                    queryForm;
    protected ContactFolio                        folio;
    protected int                                 index;
    protected IList<Contact>                      results;
    protected bool                                queryMode;
    protected int                                 queryDuration;
    protected MultiResultCallback                 callback;
    
    public ContactCRUD() : base(Gtk.WindowType.Toplevel)
    {
      this.Build();
      queryForm = new ContactQueryForm();
      folio = new ContactFolio();
      callback = new MultiResultCallback(this.ReceiveDataCallback);
      QueryMode();
      hbox.PackStart(queryForm, true, true, 2);
      hbox.PackStart(folio, true, true, 2);
      queryDuration = 0;
      folio.EnterpriseTab.OnEnterpriseEdit += new EntityEventHandler(EnterpriseEdit);
      folio.EnterpriseTab.OnEnterpriseRemove += new EntityEventHandler(EnterpriseRemove);
      folio.EnterpriseTab.OnEnterpriseUnassign += new EntityEventHandler(EnterpriseUnassign);
    } // end ctor

    public void ReceiveEvent(ServerEvent _event)
    {
      if (_event.Event == ServerEventType.Delete)
        Application.Invoke(delegate { this.RemoveEvent(_event.Entity); } );
    }

    protected void RemoveEvent(Entity entity)
    {
      if (queryMode)
        return;
      Freeze();
      if (entity is Whitemice.ZOGI.Contact)
      {
        var x = (entity as Whitemice.ZOGI.Contact);
        if (results.Contains(x))
        {
          var i = results.IndexOf(x);
          if (results.Count() == 1)
          {
            /// this is the only or last element in the contents, go into query mode
            results.Remove(x);
            QueryMode();
          }
          results.Remove(x);
          if (i == Index) 
          {
            if (i == FirstIndex)
              First();
              else Next();
          } else SensitiveNavigation();
        }
      }
      Thaw();
    }

    protected void SetTitle()
    {
      string message;
      
      if (queryMode)
      {
        message = "Query Mode";
      } else
        {
          /* Data Mode */
          message = string.Format("Contact {0} of {1} [{2}, {3}]",
                      (index + 1),
                      results.Count(),
                      Current.LastName,
                      Current.FirstName);
        }
      Title = string.Format("Contace QBE CRUD:{0}", message);
      if (queryDuration > 0)
        message = string.Format("{0} (Query executation time: {1} seconds)",
                    message,
                    queryDuration);
      queryDuration = 0;
      status.Text = message;
    } // end SetTitle

    public int Index
    {
      get { return index; }
    }

    public int FirstIndex
    {
      get { return 0; }
    }

    public int NextIndex
    {
      get
      {
        if (!queryMode)
        {
          if (Index < LastIndex)
            return (Index + 1);
          else return LastIndex;
        }
        return 0;
      }
    }

    public int PreviousIndex
    {
      get
      {
        if (!queryMode)
        {
          if (Index > FirstIndex)
            return (Index - 1);
          else return FirstIndex;
        }
        return 0;
      }
    }    

    public int LastIndex
    {
      get
      {
        if (!queryMode)
          return (results.Count() - 1);
        return 0;
      }
    }
    
    protected void SetIndex(int _index)
    {
      index = _index;
    }
    
    protected void QueryMode()
    {
      Registry.UnregisterForNotification(this);
      results = null;
      SetIndex(0);
      queryMode = true;
      folio.Visible = false;
      queryForm.Visible = true;
      /* Change sensitivity of toolbar buttons */
      first.Sensitive = false;
      previous.Sensitive = false;
      clear.Sensitive = true;
      execute.Sensitive = true;
      create.Sensitive = false;
      update.Sensitive = false;
      delete.Sensitive = false;
      print.Sensitive = false;
      next.Sensitive = false;
      last.Sensitive = false;
      link.Sensitive = false;
      SetTitle();
    } // end QueryMode
    
    protected void DataMode()
    {
      queryMode = false;
      folio.Visible = true;
      queryForm.Visible = false;
      /* Change sensitivity of toolbar buttons */
      clear.Sensitive = true;
      execute.Sensitive = false;
      create.Sensitive = true;
      update.Sensitive = true;
      delete.Sensitive = true;
      print.Sensitive = true;
      link.Sensitive = true;
    } // end DataMode
    
    protected virtual void SensitiveNavigation()
    {
      if (enterpriseSearchDialog != null)
      {
        enterpriseSearchDialog.Destroy();
        enterpriseSearchDialog = null;
      }
      if (results.Count() > (index + 1))
      {
        next.Sensitive = true;
        last.Sensitive = true;
      } else
        {
          next.Sensitive = false;
          last.Sensitive = false;
        }
      if (index == 0)
      {
        previous.Sensitive = false;
        first.Sensitive = false;
      } else
        {
          previous.Sensitive = true;
          first.Sensitive = true;
        }
      folio.Clear();
      Registry.RegisterForNotification(Current, this);
      link.Sensitive = Current.CanModify;
      update.Sensitive = Current.CanModify;
      delete.Sensitive = Current.CanModify;
      folio.Fill(Current);
      SetTitle();
    } // end SensitiveNavigation
    
    protected void Fill(IEnumerable<Contact> _results, object _start)
    {
      this.ZLOG("Fill");
      if (_start != null)
      {
        try
        {
          var start = Convert.ToDateTime(_start);
          queryDuration = DateTime.Now.Subtract(start).Seconds;
        } catch (Exception ex)
          {
            this.ZLOG(ex.Message);
            queryDuration = 0;
          }
      }
      results = new List<Whitemice.ZOGI.Contact>(_results);
      foreach(Entity contact in results)
        Registry.RegisterForNotification(contact, this);
      DataMode();
      First();
    } // end Fill

    protected void EmptyResults(DateTime start)
    {
      queryDuration = 0;
      status.Text = "Query Complete.  No matches found.";
      Title = "Contace QBE CRUD: Query Complete.  No matches found."; 
      new MessageBox("No contacts match provided criteria.");
    }
    
    protected Contact Current
    {
      get 
      {
        this.ZLOG("Current element is {0}", index);
        return (results.ElementAt(Index)); 
      }
    } // end Current
    
    internal void Next()
    {
      SetIndex(NextIndex);
      SensitiveNavigation();
    } // end Next
    
    internal void Previous()
    {
      SetIndex(PreviousIndex);
      SensitiveNavigation();
    } // end Previous
    
    internal void First()
    {
      SetIndex(FirstIndex);
      SensitiveNavigation();
    } // end First
    
    internal void Last()
    {
      SetIndex(LastIndex);
      SensitiveNavigation();
    } // end Last
    
    protected virtual void Search()
    {
      IZOGICommand  command;
      
      command = queryForm.GetQuery(65535, DateTime.Now);
      status.Text = "Executing Query...";
      Title = "Contace QBE CRUD: Executing Query...";
      command.Callback = callback;
      Server.Connection.RunCommand(command);
    } // end Search

    public void ReceiveRPCCall(RPCEventArgs args)
    {
      var context = args.Context.ToString();
      switch(context)
      {
        case "save":
          // item was saved (update or new)
          break;
        case "delete":
          if (args.Exception == null)
            RemoveCallback(Current, null, context);
            else RemoveCallback(null, args.Exception, context);
          break;
        default:
          // Assuming callback contains search results, for search results
          // the contact should be the DateTime the search was dispatched.
          IEnumerable<Whitemice.ZOGI.Contact> payload = null;
          if (args.HasResults)
          {
            payload = args.Payload.OfType<Whitemice.ZOGI.Contact>();
            this.ZLOG("Received {0} contacts", payload.Count());
            if (payload.Count() == 0) payload = null;
          }
          if (payload == null)
            status.Text = "Query result contains no data.";
            else Fill(payload, Convert.ToDateTime(args.Context));
          break;
      }
    } // end SearchResultCallback
    
    protected void ReceiveDeletion(Entity _contact)
    {
      Whitemice.ZOGI.Contact  contact;
      if (!queryMode)
      {
        if (_contact is Whitemice.ZOGI.Contact)
          contact = (_contact as Whitemice.ZOGI.Contact);
          else return;
        if (results.Contains(contact))
        {
          results.Remove(contact);
          if (index > (results.Count - 1))
          {
            Previous();
          }
          SensitiveNavigation();
        }
      } // end if-not-query-mode
    } // end ReceiveDeletion 
    
    protected virtual void OnClose (
      object _sender, 
      System.EventArgs _e)
    {
      this.Destroy();
    }

    protected virtual void OnFirst (
      object _sender, 
      System.EventArgs _e)
    {
      First();
    }

    protected virtual void OnPrevious (
      object _sender, 
      System.EventArgs _e)
    {
      Previous();
    }

    protected virtual void OnClear (
      object _sender, 
      System.EventArgs _e)
    {
      if (queryMode)
      {
        queryForm.Clear();
      } else
        {
          QueryMode();
        }
    }

    protected virtual void OnExecute (
      object _sender, 
      System.EventArgs _e)
    {
      if (queryMode)
      {
        Search();
      }
    }

    protected virtual void OnCreate (
      object _sender, 
      System.EventArgs _e)
    {
      if (!queryMode)
      {
        var contact = new Whitemice.ZOGI.Contact();
        results.Add(contact);
        Last();
      }
    }

    protected void RemoveCallback(Entity entity, Exception exception, object context)
    {
      this.ZLOG("RemoveCallback");
      if (exception == null)
      {
        //Unregister(contact, true);
        if (results.Count() == 1)
        {
          QueryMode();
        } else
          {
            results.Remove(Current);
            if (index == 0) Next();
            else Previous();
          }
        //Thaw();
      } else
        {
          new MessageBox(exception);
          //Thaw();
        }
    } // end RemoveCallback

    protected virtual void OnUpdate (
      object _sender, 
      System.EventArgs _e)
    {
      Contact contact;
      PutCommand  command;

      if (!(queryMode))
      {
        contact = results.ElementAt(index);
        if (folio.Take(contact))
        {
          command = new PutCommand(contact, "save");
          command.Callback = callback;
          Server.Connection.RunCommand(command);
        }
      } 
    } // end OnUpdate 

    protected virtual void OnPrint (
      object _sender, 
      System.EventArgs _e)
    {
      if (!(queryMode))
        new MessageBox("Not Implemented");
    }

    protected virtual void OnNext (
      object _sender, 
      System.EventArgs _e)
    {
      if (!(queryMode))
        Next();
    }

    protected virtual void OnLast (
      object _sender, 
      System.EventArgs _e)
    {
      if (!(queryMode))
        Last();
    }

    protected virtual void OnDelete (
      object _sender, 
      System.EventArgs _e)
    {
      Contact contact;

      this.ZLOG("OnDelete");
      if (!(queryMode))
      {
        contact = Current;
        if(Server.Connection.Delete(contact))
        {
          new MessageBox("Contact deleted.", MessageType.Info);
          //results.Remove(contact);
          //Remove of contact from results should automatically happen via the server event 
        } else
          {
            new MessageBox("Contact deletion failed.", MessageType.Error);
          }
      }
    } // end OnDelete

    protected virtual void OnDeleteEvent (object o, Gtk.DeleteEventArgs args)
    {
      //Unregister();
      this.Destroy();
    }

    protected EnterpriseSearchDialog  enterpriseSearchDialog;
    protected virtual void OnEnterpriseAssign (object sender, System.EventArgs e)
    {
      this.ZLOG(string.Format("Contact#{0} enterprise link", Current.ObjectId));
      if(Current.CanModify)
      {
        if (enterpriseSearchDialog == null)
        {
          enterpriseSearchDialog = new EnterpriseSearchDialog(
            ZOGIUtility.GetWindow(this), 
            Current.ObjectId,
            "Assign enterprise to " + Current.LastName);
          enterpriseSearchDialog.SetEnterpriseId += 
            new EnterpriseSearchDialog.SetEnterpriseIdHandler(EnterpriseAssign);
        }
        enterpriseSearchDialog.Present();
      } 
    }

    public virtual void EnterpriseAssign(int enterpriseId)
    {
      //TODO: Make Enterprise Assign Asyncronous
      Entity entity;
      
      this.ZLOG("ContactId#{0} enterprise assign (EnterpriseId#{1})", 
                Current.ObjectId,
                enterpriseId);
      if (enterpriseId > 0)
      {
        entity = Server.Connection.Get(enterpriseId, 65535, true);
        if (entity == null)
        {
          this.ZLOG("Failed to retrieve EnterpriseId#{0}", enterpriseId);
        } else if (entity is Whitemice.ZOGI.Enterprise)
          {
            Current.Assign(entity);
            folio.EnterpriseTab.Fill(Current);
          } else
            {
              // TODO: Is it possible to arrive here?
            }
      }
    } // End AssignEnterprise

    protected virtual void EnterpriseEdit(
      Entity entity,
      object context, 
      Exception ex)
    {
      this.ZLOG("Contact#{0} enterprise edit (Enterprise#{1})", 
                 Current.ObjectId,
                 entity.ObjectId);
      if (entity != null)
        if (!(GUI.RaiseWindow(entity)))
          new EnterpriseWindow((entity as Enterprise)); 
    } // end EnterpriseEdit

    protected virtual void EnterpriseUnassign(
      Entity entity,
      object context, 
      Exception exception)
    {
      this.ZLOG(string.Format("Contact#{0} enterprise unassign (Enterprise#{1})", 
                              Current.ObjectId,
                              entity.ObjectId));
      if (Current.CanModify)
      {
        this.ZLOG("Applying unassignment");
        Current.Unassign(entity);
        folio.EnterpriseTab.Fill(Current);
      } else new MessageBox("You lack sufficient privilages to perform the requested action");
    } // end EnterpriseUnassign

    //TODO: Test ContactCRUD.EnterpriseRemove
    protected virtual void EnterpriseRemove(
      Entity entity,
      object context, 
      Exception ex)
    {
      this.ZLOG(string.Format("Contact#{0} enterprise remove (Enterprise#{1})", 
                              Current.ObjectId,
                              entity.ObjectId));
      if (GUI.RaiseWindow(entity))
      {
        // Object is being viewed, present the window and notify user
        new MessageBox("Object#" + entity.ObjectId.ToString() + " is currently in use.");
      } else
        {
          if (entity.CanModify)
          {
            //Freeze();
            DeleteCommand command = new DeleteCommand(entity, new object[] { ":delete" } );
            command.Callback = callback;
          } else 
            {
              // Insufficient privileges to delete object
              new MessageBox("You lack sufficient privilages to perform the requested action");
            }
        } // end else-object-is-available
    } // end EnterpriseRemove

    protected void Freeze()
    {
    }

    protected void Thaw()
    {
    }
  } // end ContractCRUD
} // end namespace
